<?php
/*
	PookiWiki - A wiki unlike the rest.
	Copyright (C) 2012 Jordan Craw

	This program is free software; you can redistribute it and/or
	modify it under the terms of the GNU General Public License
	as published by the Free Software Foundation; either version 2
	of the License, or (at your option) any later version.

	This program is distributed in the hope that it will be useful,
	but WITHOUT ANY WARRANTY; without even the implied warranty of
	MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
	GNU General Public License for more details.

	You should have received a copy of the GNU General Public License
	along with this program; if not, write to the Free Software
	Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
*/

	class PookiSQLConnection
	{
		public 		$PDOInstance;
		public		$current_driver;
		public 		$status;
		public		$server_version;
		public 		$persistent;
		
		private 	$connection_credentials;
		
		protected function checkDriverAvailable($driver)
		{
			$supported_drivers = array_diff(PDO::getAvailableDrivers(),array("odbc","mssql"));
			return in_array($driver,$supported_drivers);
		}

		protected function validateStatus($status_string)
		{
			return strpos($status_string,"TCP/IP") ? POOKIWIKI_SQL_CONNECTION_STATUS_CONNECTED_NON_PERSISTENT : POOKIWIKI_SQL_CONNECTION_STATUS_NOT_CONNECTED_UNREACHABLE;
		}
		
		public function __construct(PookiConnectionSettings $settings)
		{
			$this->connection_credentials = $settings;
			
			
			if($this->checkDriverAvailable($settings->connection_type))
			{
				$pdo_connection = $settings->connection_type . ":host=" . $settings->URL . ";dbname=" . $settings->dbname . ";";

				try
				{

					$pdo_contains 			= new PDO($pdo_connection,$settings->username,$settings->password);
					$this->status			= $this->validateStatus($pdo_contains->getAttribute(constant("PDO::ATTR_CONNECTION_STATUS")));
					$this->server_version	= $pdo_contains->getAttribute(constant("PDO::ATTR_SERVER_VERSION"));
					$this->persistent		= $pdo_contains->getAttribute(constant("PDO::ATTR_PERSISTENT"));
					$this->current_driver	= $pdo_contains->getAttribute(constant("PDO::ATTR_DRIVER_NAME"));
					
					$this->PDOInstance = new PookiPDOWrapper($pdo_contains);

				}
				catch (PDOException $exception)
				{
						var_dump($exception);
						$this_error = $exception->getCode();
				
						if(($this_error >= 1037 and $this_error <= 1041) OR ($this_error >=1043 and $this_error <=1045))
						{
							$this->status = POOKIWIKI_SQL_CONNECTION_STATUS_NOT_CONNECTED_ERROR;
						}
						if($this_error == 1042)
						{
							$this->status = POOKIWIKI_SQL_CONNECTION_STATUS_NOT_REACHABLE;
						}
						
						echo $this->status;
				}
			}
		}
	}
	
	class PookiPDOWrapper
	{
		public $PDO_connection;
		
		public function __construct($wrapper)
		{
			$this->PDO_Connection = $wrapper;
		}
		
	}
	
	class PookiQuery
	{
		public $query_string;
		public $query_type;
		public $query_instance;
		public $query_results;
		public $status;
		
		
		public function __construct($query_string,$query_type,$status = POOKIWIKI_SQL_QUERY_STATUS_UNQUERIED)
		{
			$this->query_string = $query_string;
			$this->query_type = $query_type;
			$this->status = $status;
		}
		
	}
	
	class PookiSQLLoader
	{
		public			$use_transactions 		= TRUE;
		protected 		$connections 			= array();
		protected		$queries				= array();
		protected 		$pdo_instance;
		
		protected $unsupported_query_statements = array(
			"HANDLER", // For now, not needed because I'm just not bothering to access this stuff.
			"LOAD DATA INFILE",
			"START","COMMIT","ROLLBACK","SAVEPOINT","ROLLBACK_TO_SAVEPOINT","LOCK_TABLES","UNLOCK_TABLES","SET_TRANSACTION" // Used to prevent usage of transactions outside of PDO
		);
		
		protected $pattern_matches = array
		(
			POOKIWIKI_SQL_QUERY_TYPE_DEFINITION_STATEMENT => array("ALTER","CREATE","DROP","RENAME","TRUNCATE"),
			POOKIWIKI_SQL_QUERY_TYPE_MANIPULATION_STATEMENT => array("CALL","DELETE","DO","HANDLER","INSERT","LOAD_DATA_INFILE","LOAD_XML","REPLACE","SELECT","UPDATE"),
			POOKIWIKI_SQL_QUERY_TYPE_TRANSACTION_LOCKING_STATEMENT => array("START","COMMIT","ROLLBACK","SAVEPOINT","ROLLBACK_TO_SAVEPOINT","LOCK_TABLES","UNLOCK_TABLES","SET_TRANSACTION"),
			POOKIWIKI_SQL_QUERY_TYPE_UNSUPPORTED_STATEMENT => array()
		);
		
		protected $query_states = array
		(
			POOKIWIKI_SQL_QUERY_STATUS_UNQUERIED,
			POOKIWIKI_SQL_QUERY_STATUS_QUERIED_WITH_RESULT,
			POOKIWIKI_SQL_QUERY_STATUS_QUERIED_WITHOUT_RESULT,
			POOKIWIKI_SQL_QUERY_STATUS_QUERIED_WITH_ERROR
		);
		
		
		public function __construct(PookiSettings $settings)
		{
			$connection 			= new PookiSQLConnection($settings->simpleGetSettings());
			$this->connections[] 	= $connection;
			$this_pdo				= $connection->PDOInstance;
			//$this->pdo_instance		= $this_pdo->PDO_Connection;
		}
		
		protected function beginTransaction()
		{
			if($this->use_transactions)
			{
				if(!$this->pdo_instance->inTransaction())
				{
					$this->pdo_instance->beginTransaction();
				}
			}
		}
		
		protected function commitTransaction()
		{
			if($this->use_transactions)
			{
				if($this->pdo_instance->inTransaction())
				{
					$this->pdo_instance->commit();
				}
			}
		}
		
		public function rollbackTransaction()
		{
			if($this->pdo_instance->inTransaction())
			{
				$this->pdo_instance->rollBack();
			}
		}
		
		protected function addQuery($query_string,$query_type,$query_status = POOKIWIKI_SQL_QUERY_STATUS_UNQUERIED)
		{
			if(strlen($query_string) > 0 && in_array($query_type,array_keys($this->pattern_matches)))
			{
				$new_query = new PookiQuery($query_string,$query_type,$query_status);
				$this->queries[] = $new_query;
			}
			
		}
		
		protected function differentiateQuery($query_string)
		{
			if(strlen($query_string))
			{
				$match_patterns = $this->pattern_matches;
				if(preg_match("#^(ALTER|SELECT|DROP|RENAME|CREATE|TRUNCATE|CALL|DELETE|DO|HANDLER|INSERT|LOAD|REPLACE|SELECT|UPDATE)\s(([\w.,\[\](){}@=_-\|\'<>=*]+\s?)+)#",$query_string,$query_matches))
				{
					if(count($query_matches) >=2 & isset($query_matches[0]) && isset($query_matches[1]))
					{
						$query_corrected = $query_matches[0];
						$query_method = strtoupper($query_matches[1]);
						$query_type = NULL;
						
						if(in_array($query_method,$this->pattern_matches[POOKIWIKI_SQL_QUERY_TYPE_DEFINITION_STATEMENT]))
						{
							$query_type = POOKIWIKI_SQL_QUERY_TYPE_DEFINITION_STATEMENT;
						}
						if(in_array($query_method,$this->pattern_matches[POOKIWIKI_SQL_QUERY_TYPE_MANIPULATION_STATEMENT]))
						{
							$query_type = POOKIWIKI_SQL_QUERY_TYPE_MANIPULATION_STATEMENT;
						}
						else if(in_array($query_method,$this->pattern_matches[POOKIWIKI_SQL_QUERY_TYPE_UNSUPPORTED_STATEMENT]))
						{
							$query_type = POOKIWIKI_SQL_QUERY_TYPE_DEFINITION_STATEMENT;
						}
						else 
						{
							$query_type = POOKIWIKI_SQL_QUERY_TYPE_UNSUPPORTED_STATEMENT;
						}
						
						$this->addQuery($query_corrected, $query_type);
					}
				}
				else
				{
					throw new PookiSQLException();
				}
			}
		}
		
		protected function getLastQuery()
		{
			return count($this->queries) ? array_pop($this->queries) : FALSE;
		}
		
		
		public function query($query_string)
		{
			$this->differentiateQuery($query_string);
			$query = $this->getLastQuery();
			
			if($query != FALSE)
			{
				$this->beginTransaction(); // TODO: Implement the "execute stuff" part of PDO, which isn't very useful for my purposes, but will result in whining, nonetheless
				$result_array = array();
				
				if($this->pdo_instance->query($query_string))
				{
					foreach($this->pdo_instance->query($query_string) as $row)
					{
						$row_values = array_unique(array_values($row));
						$result_array = $row_values;
					}
					$query->query_results = $result_array;
				}
				else
				{
					throw new PookiSQLError();
				}
				
			}
			else
			{
				throw new PookiSQLError();
			}
		}
	}

	
/*	class PookiSQLSentinel
	{
		// Security-checking class for much, much later.
	}
*/
	final class PookiSQL
	{
	
	}
?>